Skip to content

Add async StudioMember binding protocol#548

Draft
louis4li wants to merge 17 commits into
devfrom
feat/2026-04-30_studio-member-async-bind
Draft

Add async StudioMember binding protocol#548
louis4li wants to merge 17 commits into
devfrom
feat/2026-04-30_studio-member-async-bind

Conversation

@louis4li
Copy link
Copy Markdown
Contributor

@louis4li louis4li commented Apr 30, 2026

Summary

Implements the StudioMember async binding protocol from issue #516.

  • Adds StudioMember actor-owned binding run admission and execution state.
  • Moves bind API semantics to an honest accepted receipt plus run status polling.
  • Adds frontend polling/refresh handling for accepted binding runs.
  • Removes process-local platform binding execution from StartAsync; the run actor now drives platform binding through self continuation and can recover pending runs from persisted state.
  • Adds a binding-run current-state read model/query port so the status endpoint reads the run actor's authority-shaped projection instead of member readmodel summaries.
  • Keeps platform binding completion asynchronous: ExecuteAsync now returns after starting the platform work, and completion/failure flows back through typed continuation events.
  • Promotes GAgent endpoint kind from an internal string to a typed StudioMember proto enum before mapping to GAgent Service endpoint kind.

Scope Notes

This PR intentionally keeps the fix scoped to StudioMember async binding correctness. It does not actorize or redesign the broader legacy ScopeBinding command service.

Adjacent architecture issues are tracked separately in #543-#547 and are not prerequisites for this async binding protocol unless a later change explicitly wires them into this flow.

Follow-up #616 tracks replacing ScopeBindingCommandApplicationService readmodel visibility polling with an explicit readiness/observation contract. That cleanup is derivative architecture debt; PR #548 is functional without expanding into it.

Failure codes remain open strings for now because current control flow branches on typed run status, while failure code is only diagnostic/display data. If retry/API behavior starts branching by reason, we should promote the reason to a typed enum plus detail fields.

Smoke Test

Ran Aevatar.Mainnet.Host.Api locally on http://127.0.0.1:5101 with auth disabled and in-memory runtime/projection providers.

  • GET /api/health returned 200 ready.
  • POST /api/scopes/{scopeId}/members returned 201.
  • PUT /api/scopes/{scopeId}/members/{memberId}/binding returned accepted with binding run bind-d0f71c089c944c88be8276e65050f300.
  • GET /api/scopes/{scopeId}/members/{memberId}/binding-runs/{bindingRunId} reached status: "succeeded".
  • GET /api/scopes/{scopeId}/members/{memberId}/binding returned lastBinding.revisionId: "rev-smoke-gagent-1".
  • GET /api/scopes/{scopeId}/members/{memberId}/endpoints/chat/contract returned 200.

Validation

  • dotnet build src/Aevatar.Mainnet.Host.Api/Aevatar.Mainnet.Host.Api.csproj --nologo — passed with existing warnings.
  • dotnet test test/Aevatar.Studio.Tests/Aevatar.Studio.Tests.csproj --no-restore --nologo --filter "ScopeBindingStudioMemberPlatformBindingCommandServiceTests|ActorDispatchStudioMemberCommandServiceTests|StudioMemberBindingRunGAgentStateTests|StudioMemberServiceBindingTests" — 37 passed.
  • dotnet test test/Aevatar.Studio.Tests/Aevatar.Studio.Tests.csproj --no-restore --nologo — 473 passed.
  • bash tools/ci/test_stability_guards.sh — passed.
  • bash tools/ci/architecture_guards.sh — passed.

Related

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 74.80438% with 161 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.89%. Comparing base (36f96f7) to head (5c376ac).

Files with missing lines Patch % Lines
...indingStudioMemberPlatformBindingCommandService.cs 64.25% 60 Missing and 14 partials ⚠️
...Application/Studio/Services/StudioMemberService.cs 78.65% 12 Missing and 7 partials ⚠️
...tion/QueryPorts/ProjectionStudioMemberQueryPort.cs 52.94% 13 Missing and 3 partials ⚠️
...ors/StudioMemberBindingRunCurrentStateProjector.cs 72.22% 9 Missing and 6 partials ⚠️
...ervices/ActorDispatchStudioMemberCommandService.cs 84.41% 4 Missing and 8 partials ⚠️
...Ports/ProjectionStudioMemberBindingRunQueryPort.cs 82.00% 6 Missing and 3 partials ⚠️
...on/Projectors/StudioMemberCurrentStateProjector.cs 46.66% 5 Missing and 3 partials ⚠️
...ojection/Mapping/MemberImplementationKindMapper.cs 36.36% 6 Missing and 1 partial ⚠️
.../Bindings/ScopeBindingCommandApplicationService.cs 87.50% 0 Missing and 1 partial ⚠️
@@            Coverage Diff             @@
##              dev     #548      +/-   ##
==========================================
- Coverage   72.01%   71.89%   -0.13%     
==========================================
  Files        1255     1261       +6     
  Lines       90723    91185     +462     
  Branches    11877    11929      +52     
==========================================
+ Hits        65338    65561     +223     
- Misses      20703    20920     +217     
- Partials     4682     4704      +22     
Flag Coverage Δ
ci 71.89% <74.80%> (-0.13%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...actions/StudioMemberBindingRunNotFoundException.cs 100.00% <100.00%> (ø)
...io.Application/Studio/Contracts/MemberContracts.cs 81.39% <100.00%> (+3.91%) ⬆️
....Studio.Hosting/Endpoints/StudioMemberEndpoints.cs 73.20% <100.00%> (+4.66%) ⬆️
...oProjectionReadModelServiceCollectionExtensions.cs 92.02% <100.00%> (+0.23%) ⬆️
...DependencyInjection/ServiceCollectionExtensions.cs 100.00% <100.00%> (ø)
...rBindingRunCurrentStateDocumentMetadataProvider.cs 100.00% <100.00%> (ø)
...dioMemberBindingRunCurrentStateDocument.Partial.cs 100.00% <100.00%> (ø)
...e.Abstractions/ScopeBindings/ScopeBindingModels.cs 100.00% <100.00%> (ø)
...Service.Hosting/Endpoints/ScopeServiceEndpoints.cs 77.40% <ø> (-4.60%) ⬇️
.../Bindings/ScopeBindingCommandApplicationService.cs 90.37% <87.50%> (+1.08%) ⬆️
... and 8 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@louis4li
Copy link
Copy Markdown
Contributor Author

Updated this PR with the scoped fixes we discussed.

What changed in the latest push (cbbcbb77):

  • ExecuteAsync now starts platform binding work and returns immediately; completion/failure is delivered back to the binding-run actor through continuation events.
  • Removed the extra pre-scheduled 30s platform execution retry so the same PlatformBindingCommandId is not duplicated.
  • Allowed the success continuation handler to process self/direct continuation envelopes.
  • Changed StudioMember GAgent endpoint kind to a typed proto enum and fail fast on unsupported kinds at the application boundary.

Smoke test against local Aevatar.Mainnet.Host.Api on 127.0.0.1:5101 passed: health 200, create member 201, bind accepted, run reached succeeded, lastBinding populated with rev-smoke-gagent-1, and endpoint contract returned 200.

Validation run:

  • dotnet build src/Aevatar.Mainnet.Host.Api/Aevatar.Mainnet.Host.Api.csproj --nologo
  • dotnet test test/Aevatar.Studio.Tests/Aevatar.Studio.Tests.csproj --no-restore --nologo --filter "ScopeBindingStudioMemberPlatformBindingCommandServiceTests|ActorDispatchStudioMemberCommandServiceTests|StudioMemberBindingRunGAgentStateTests|StudioMemberServiceBindingTests"
  • dotnet test test/Aevatar.Studio.Tests/Aevatar.Studio.Tests.csproj --no-restore --nologo
  • bash tools/ci/test_stability_guards.sh
  • bash tools/ci/architecture_guards.sh

I also created follow-up #616 for replacing the legacy ScopeBindingCommandApplicationService readmodel visibility polling with an explicit readiness/observation contract. That stays out of this PR so #548 remains focused on the StudioMember async binding protocol.

@louis4li
Copy link
Copy Markdown
Contributor Author

Small follow-up after review: I removed AllowSelfHandling = true from HandlePlatformBindingSucceeded in 5c376aca.

Reason: the successful platform binding continuation is dispatched with a direct route from ScopeBindingStudioMemberPlatformBindingCommandService, whose publisher id is the command service route name, not the binding-run actor itself. So self-handling is not required for the success path. Keeping it would broaden the handler surface unnecessarily and could imply a self-published success path that this PR does not need.

I kept the existing failure self-handling check because failure can still be produced internally by the run actor when the platform binding port is unavailable.

Validation after removing it:

  • dotnet test test/Aevatar.Studio.Tests/Aevatar.Studio.Tests.csproj --no-restore --nologo --filter "ScopeBindingStudioMemberPlatformBindingCommandServiceTests|ActorDispatchStudioMemberCommandServiceTests|StudioMemberBindingRunGAgentStateTests|StudioMemberServiceBindingTests" — 36 passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor StudioMember binding admission into actor-owned async protocol

2 participants